package com.example.demo.service.serviceImpl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.demo.enums.RealNameAuthenticationEnums;
import com.example.demo.enums.ValidationEnums;
import com.example.demo.mapper.*;
import com.example.demo.pojo.Admin.Admin;
import com.example.demo.pojo.Admin.AdminLoginLog;
import com.example.demo.pojo.Competitions.Competitions;
import com.example.demo.pojo.Email.Validation;
import com.example.demo.pojo.R;
import com.example.demo.pojo.RealNameAuthentication;
import com.example.demo.pojo.User.*;
import com.example.demo.pojo.vip.VipDto;
import com.example.demo.pojo.vip.VipUserDto;
import com.example.demo.service.UserService;
import com.example.demo.utils.AsyncServiceUtils.LoginAsyncService;
import com.example.demo.utils.IpUtils.AddressUtils;
import com.example.demo.utils.IpUtils.IpUtil;
import com.example.demo.utils.LoginCount;
import com.example.demo.utils.PcUtils.UploadFileUtils;
import com.example.demo.utils.TokenUtil.JwtUtils;
import com.example.demo.utils.TokenUtil.OnlineUserStatsService;
import com.example.demo.utils.TokenUtil.decryptToken;
import com.example.demo.utils.TokenUtil.spiltUtils;
import com.example.demo.utils.UserUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.formula.functions.T;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.Part;
import java.io.IOException;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.*;

import static com.example.demo.utils.ConstantUtils.*;
import static com.example.demo.utils.TokenUtil.HasLoginToken.hasLoginToken;

@Service
@Slf4j
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
    @Resource
    private LoginCount loginCount;
    @Resource
    private UserMapper userMapper;

    @Resource
    private AdminMapper adminMapper;

    @Resource
    private CompetitionMapper competitionMapper;
    @Resource
    private OnlineUserStatsService onlineUserStatsService;

    @Autowired
    private LoginAsyncService loginAsyncService;
    @Resource
    private RealNameAuthenticationMapper realNameAuthenticationMapper;

    @Resource
    private ValidationMapper validationMapper;
    @Resource
    private UserUtil userUtil;
    @Resource
    private BCryptPasswordEncoder bCryptPasswordEncoder;  //注入bcrypt加密

    @Resource
    private VipDtoMapper vipDtoMapper;

    @Resource
    private LoginLogMapper loginLogMapper;


    @Resource
    private AdminLoginLogMapper adminLoginLogMapper;

    @Override
    public R<T> userLogin(LoginRequest loginRequest,
                          HttpServletResponse response,
                          HttpServletRequest request) {
        // 初始化登录日志实体
        LoginLog loginLog = new LoginLog();
        String ip = IpUtil.getIpAddr(request);
        //判断用户是否多次登录失败
        if (loginCount.getLoginCount(loginRequest.getUserName()) > MAX_ATTEMPT) {
            Integer loginTime = loginCount.getLoginTime(loginRequest.getUserName());
            return R.failed("你登录次数过多，请" + loginTime + "分钟后再登录！");
        }
        //判断用户名密码是否为空
        if (StringUtils.isEmpty(loginRequest.getUserName()) || StringUtils.isEmpty(loginRequest.getPassWord())) {
            //添加登录失败记录
            loginCount.addCounts(loginRequest.getUserName());
            return R.ACCOUNT();
        }
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("user_name", loginRequest.getUserName());
        //查询是否有此用户
        User login = userMapper.selectOne(queryWrapper);
        if (login == null) {
            return R.failed();
        }
        // 检查是否已经有有效的登录Cookie
        if (hasLoginToken(request)) {
            //在线获取
            String realAddress = AddressUtils.getRealAddressByIP(ip);
            loginLog.setLoginIp(request.getRemoteAddr());
            loginLog.setLoginAddress(realAddress);
            loginLog.setUserId(login.getId()); // 设置用户ID
            loginLog.setLoginTime(new Date()); // 设置登录时间
            loginLog.setLoginResult("你已经登录过了，请退出后重试！");
            //保存登录日志
            loginAsyncService.saveLoginLog(loginLog);
            return R.success("你已经登录过了，请退出后重试！");
        }
        //判断用户是否被禁用
        if (login.getStatus() == STATUS_DISABLED) {
            //在线获取
            String realAddress = AddressUtils.getRealAddressByIP(ip);
            loginLog.setLoginIp(request.getRemoteAddr());
            loginLog.setLoginAddress(realAddress);
            loginLog.setUserId(login.getId()); // 设置用户ID
            loginLog.setLoginTime(new Date()); // 设置登录时间
            loginLog.setLoginResult("你的账户被冻结，请联系管理员！");
            //保存登录日志
            loginAsyncService.saveLoginLog(loginLog);
            return R.failed("你的账户被冻结，请联系管理员！");
        }
        // 判断密码是否正确
        if (!bCryptPasswordEncoder.matches(loginRequest.getPassWord(), login.getPassWord())) {
            // 添加登录失败记录
            loginCount.addCounts(loginRequest.getUserName());
            //在线获取
            String realAddress = AddressUtils.getRealAddressByIP(ip);
            loginLog.setLoginIp(request.getRemoteAddr());
            loginLog.setLoginAddress(realAddress);
            loginLog.setUserId(login.getId()); // 设置用户ID
            loginLog.setLoginTime(new Date()); // 设置登录时间
            loginLog.setLoginResult("密码错误！");
            //保存登录日志
            loginAsyncService.saveLoginLog(loginLog);
            return R.failed("密码错误！");
        }

        //在线获取
        String realAddress = AddressUtils.getRealAddressByIP(ip);
        loginLog.setLoginIp(request.getRemoteAddr());
        loginLog.setLoginAddress(realAddress);
        loginLog.setUserId(login.getId()); // 设置用户ID
        loginLog.setLoginTime(new Date()); // 设置登录时间
        //组装token
        String token = JwtUtils.createToken(UUID.randomUUID() + ":" + loginRequest.getUserName());
        // 将 token 存入 Cookie 中
        if (token != null) {
            Cookie cookie = new Cookie("token", token);
            cookie.setPath("/");
            cookie.setHttpOnly(true);
            cookie.setMaxAge(COOKIE_MAX_AGE);
            response.addCookie(cookie);
//            // 创建Cookie字符串
//            String cookieValue = COOKIE_NAME + "=" + token
//                    + "; Path=/"
//                    + "; HttpOnly=true"
//                    + "; Secure=false"
//                    + "; Max-Age=" + COOKIE_MAX_AGE
//                    + "; SameSite=None"; // 添加 SameSite 属性
//
//            // 直接设置响应头来添加Cookie，确保包含SameSite属性
//            response.setHeader("Set-Cookie", cookieValue);
            loginLog.setLoginResult("登录成功");
            onlineUserStatsService.online(login.getId());
            //保存登录日志
            loginAsyncService.saveLoginLog(loginLog);
            return R.success("登录成功！");
        }
        loginLog.setLoginResult("登录失败！");
        //保存登录日志
        loginAsyncService.saveLoginLog(loginLog);
        return R.failed("登录失败！");
    }

    @Override
    public R<T> adminLogin(LoginRequest loginRequest, HttpServletResponse response, HttpServletRequest request) {
        QueryWrapper<Admin> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("username", loginRequest.getUserName());
        //查询是否有此用户
        Admin login = adminMapper.selectOne(queryWrapper);
        if (login == null) {
            return R.failed("没有此管理员！");
        }
        //判断密码是否正确
        if (!login.getPassword().equals(loginRequest.getPassWord())) {
            return R.failed("管理员密码错误！");
        }
        // 初始化登录日志实体
        AdminLoginLog adminLoginLog = new AdminLoginLog();
        // 假设你有方法根据用户名获取用户ID
        adminLoginLog.setUserId(login.getId()); // 设置用户ID
        adminLoginLog.setLoginTime(new Date()); // 设置登录时间
        String ip = IpUtil.getIpAddr(request);
        //在线获取
        String realAddress = AddressUtils.getRealAddressByIP(ip);
        adminLoginLog.setLoginIp(request.getRemoteAddr());
        adminLoginLog.setLoginAddress(realAddress);
        //组装token
        String token = JwtUtils.createToken(UUID.randomUUID() + ":" + loginRequest.getUserName());
        // 将 token 存入 Cookie 中
        if (token != null) {
            Cookie cookie = new Cookie("token", token);
            cookie.setPath("/");
            cookie.setHttpOnly(true);
            cookie.setMaxAge(COOKIE_MAX_AGE);
            response.addCookie(cookie);
//            // 创建Cookie字符串
//            String cookieValue = COOKIE_NAME + "=" + token
//                    + "; Path=/"
//                    + "; HttpOnly=true"
//                    + "; Secure=false"
//                    + "; Max-Age=" + COOKIE_MAX_AGE
//                    + "; SameSite=None"; // 添加 SameSite 属性
//
//            // 直接设置响应头来添加Cookie，确保包含SameSite属性
//            response.setHeader("Set-Cookie", cookieValue);
            onlineUserStatsService.adminOnline(login.getId());
            adminLoginLog.setLoginResult("登录成功！");
            //保存登录日志
            loginAsyncService.saveAdminLoginLog(adminLoginLog);
            return R.success("登录成功！");
        }
        adminLoginLog.setLoginResult("登录失败！");
        //保存登录日志
        loginAsyncService.saveAdminLoginLog(adminLoginLog);
        return R.failed("登录失败！");
    }

    @Override
    public R<T> outLogin(String token, HttpServletResponse response) {
        String pe = decryptToken.decrypt(token);
        if (pe != null) {
            String userName = spiltUtils.getUserName(pe);
            Integer userId = userUtil.getUserId(userName);

            Cookie cookie = new Cookie("token", null);
            cookie.setPath("/");
            cookie.setHttpOnly(true);
            cookie.setMaxAge(0);
            response.addCookie(cookie);

            onlineUserStatsService.outOnline(userId);
            return R.success("退出登录成功！");
        }
        return R.failed("您还没有登录！");
    }

    @Override
    public R<T> adminOtLogin(String token, HttpServletResponse response) {
        String pe = decryptToken.decrypt(token);
        if (pe != null) {
            String userName = spiltUtils.getUserName(pe);
            Integer userId = userUtil.getUserId(userName);

            Cookie cookie = new Cookie("token", null);
            cookie.setPath("/");
            cookie.setHttpOnly(true);
            cookie.setMaxAge(0);
            response.addCookie(cookie);

            onlineUserStatsService.outOnline(userId);
            return R.success("退出登录成功！");
        }
        return R.failed("您还没有登录！");
    }

    @Override
    @Transactional
    public R<T> register(User user, HttpServletRequest request) {
        if (StringUtils.isBlank(user.getUserName()) || StringUtils.isBlank(user.getPassWord())) {
            return R.ACCOUNT();
        }
        if (Objects.equals(user.getRole(), "vip")) {
            return R.failed("注册失败！");
        }
        //查询是否存在同名的用户
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper
                .eq("user_name", user.getUserName());
        User existUser = userMapper.selectOne(queryWrapper);

        if (existUser != null) {
            return R.PARAMS();
        }
        // 所有验证通过后，处理文件上传
        try {
            Part headerFilePart = request.getPart("avatar");
            if (headerFilePart != null) {
                UploadFileUtils.FileResult result = UploadFileUtils.savePart(user.getUserName(), request, headerFilePart);
                user.setHeaderUrl(result.getUrl());
            }
        } catch (IOException | ServletException e) {
            return R.failed("注册失败！");
        }
        //加密密码
        user.setPassWord(bCryptPasswordEncoder.encode(user.getPassWord()));
        user.setCreateTime(new Date());
        user.setRole(USER_ROLE);
        int insert = userMapper.insert(user);
        if (insert > 0) {
            //生成实名表
            RealNameAuthentication realNameAuthentication = new RealNameAuthentication();
            realNameAuthentication.setUserId(user.getId());
            realNameAuthentication.setRealNameStatus(RealNameAuthenticationEnums.UNAUTHENTICATED.getStatus());
            int realNameAuthenticationInsert = realNameAuthenticationMapper.insert(realNameAuthentication);
            if (realNameAuthenticationInsert > 0) {
                return R.success("注册成功！");
            }
            return R.failed("注册失败！");
        }
        return R.failed("注册失败！");
    }

    @Override
    public R<T> findPassWord(String email, String newUserName, String newPassWord, String code) {
        // 首先，检查用户是否存在
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("email", email);
        User user = userMapper.selectOne(queryWrapper);
        if (user != null) {
            // 检查验证码是否正确且有效
            Date date = new Date();
            QueryWrapper<Validation> validationWrapper = new QueryWrapper<>();
            validationWrapper.eq("code", code);
            validationWrapper.ge("time", date);
            validationWrapper.eq("type", ValidationEnums.FIND_PASSWORD.getType());
            Validation validation = validationMapper.selectOne(validationWrapper);
            if (validation != null) {
                UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
                updateWrapper.eq("email", email) // 确定要更新的用户
                        .set("pass_word", bCryptPasswordEncoder.encode(newPassWord)) // 更新密码
                        .set("user_name", newUserName); // 更新用户名
                int updateCount = userMapper.update(null, updateWrapper);
                if (updateCount > 0) {
                    return R.success("修改成功！");
                } else {
                    return R.failed("修改失败！");
                }
            } else {
                return R.failed("验证码错误或者已经失效！");
            }
        } else {
            return R.failed("没有此用户！");
        }
    }

    /**
     * 修改用户头像
     *
     * @param user    用户信息
     * @param request
     * @return
     */
    @Override
    public R<T> updateUserAvatar(User user, HttpServletRequest request, String token) {
        //获取当前用户的用户名
        String decrypt = decryptToken.decrypt(token);
        String userName = spiltUtils.getUserName(decrypt);
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("user_name", userName);
        User existingUser = userMapper.selectOne(queryWrapper);
        if (existingUser == null) {
            return R.failed("用户不存在！");
        }
        int update = userMapper.update(user, queryWrapper);
        if (update > 0) {
            return R.success(user.getHeaderUrl());
        } else {
            return R.failed("更新头像失败!");
        }
    }

    @Override
    public R<UserInfoDTO> queryCurrentUserInfo(String token) {
        //获取当前用户的用户名
        String decrypt = decryptToken.decrypt(token);
        String userName = spiltUtils.getUserName(decrypt);
        //查询当前用户所有信息
        UserInfoDTO userInfo = userMapper.getUserInfo(userName);
        return R.success(userInfo);

    }

    @Override
    public R<T> updateUserInfo(String token, User user) {
        int update = userMapper.updateById(user);
        if (update > 0) {
            return R.success("修改成功!");
        }
        return R.failed("修改失败!");
    }

    @Override
    public R<T> clearUser(String token, Integer time) {
        Long clear = onlineUserStatsService.clear(Duration.ofDays(time));
        if (clear > 0) {
            return R.success("清理成功！");
        }
        return R.failed("清理失败！");
    }


    @Override
    public User getByUserName(String userName) {
        return lambdaQuery().eq(User::getUserName, userName).one();
    }

    @Override
    public Admin getByAdminUserName(String username) {
        QueryWrapper<Admin> adminQueryWrapper = new QueryWrapper<>();
        adminQueryWrapper.eq("username", username);
        return adminMapper.selectOne(adminQueryWrapper);
    }

    @Override
    public R<Map<String, Object>> getAllUser(int pageNum, int pageSize) {
        Page<User> page = new Page<>(pageNum, pageSize, true);
        QueryWrapper<User> userQueryWrapper = new QueryWrapper<>();
        IPage<User> userPage = userMapper.selectPage(page, userQueryWrapper);
        List<User> users = userPage.getRecords();
        long totalCount = userPage.getTotal();

        // 使用 Log 记录日志
        log.info("总共找到记录数：{}", totalCount);
        log.info("当前页码：{}", page.getCurrent());
        log.info("总页数：{}", page.getPages());

        Map<String, Object> result = new HashMap<>();
        result.put("users", users);
        result.put("totalCount", totalCount);

        return R.success(result);
    }

    @Override
    public R<Map<String, Object>> getAllCompetition(int pageNum, int pageSize) {
        Page<Competitions> page = new Page<>(pageNum, pageSize, true);
        QueryWrapper<Competitions> competitionsQueryWrapper = new QueryWrapper<>();
        IPage<Competitions> competitionsPage = competitionMapper.selectPage(page, competitionsQueryWrapper);
        List<Competitions> competitionsList = competitionsPage.getRecords();
        long totalCount = competitionsPage.getTotal();

        // 使用 Log 记录日志
        log.info("总共找到记录数：{}", totalCount);
        log.info("当前页码：{}", page.getCurrent());
        log.info("总页数：{}", page.getPages());

        Map<String, Object> result = new HashMap<>();
        result.put("competitions", competitionsList);
        result.put("totalCount", totalCount);

        return R.success(result);
    }

    @Override
    public R<User> getUserById(Integer id) {
        QueryWrapper<User> userQueryWrapper = new QueryWrapper<>();
        userQueryWrapper.eq("id", id);
        User user = userMapper.selectOne(userQueryWrapper);
        if (user != null) {
            return R.success(user);
        }
        return R.failed("此用户不存在！或被删除！");
    }

    @Override
    public R<T> banUserById(Integer id) {
        QueryWrapper<User> userQueryWrapper = new QueryWrapper<>();
        userQueryWrapper.eq("id", id);
        User user = userMapper.selectOne(userQueryWrapper);
        if (user.getStatus() == BAN) {
            return R.failed("此用户已被禁用！");
        }
        UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
        updateWrapper.eq("id", id) // 确定要禁用的用户
                .set("status", BAN); // 更新状态
        int updateCount = userMapper.update(null, updateWrapper);
        if (updateCount > 0) {
            return R.success("用户禁用成功！");
        }
        return R.failed("用户禁用失败！");
    }

    @Override
    public R<Long> getCountTodayActiveUsers() {
        return R.success(onlineUserStatsService.countTodayActiveUsers());
    }

    @Override
    public R<Long> getCountYesterdayActiveUsers() {
        return R.success(onlineUserStatsService.countYesterdayActiveUsers());
    }

    @Override
    public R<Long> getActiveUsersCounts(Integer time) {
        return R.success(onlineUserStatsService.count(Duration.ofDays(time)));
    }

    @Override
    public R<List<VipUserDto>> getExpireVipUsers() {
        return R.success(userMapper.getExpireVipUsers());
    }

    @Override
    public R<Long> getUserVipExpireDay(String token) {
        LocalDateTime now = LocalDateTime.now();
        //获取当前用户的用户名
        String decrypt = decryptToken.decrypt(token);
        String userName = spiltUtils.getUserName(decrypt);
        Integer userId = userUtil.getUserId(userName);
        //查询用户vip表
        QueryWrapper<VipDto> vipDtoQueryWrapper = new QueryWrapper<>();
        vipDtoQueryWrapper.eq("user_id", userId).eq("status", UNEXPIRED);
        VipDto vipDto = vipDtoMapper.selectOne(vipDtoQueryWrapper);
        if (vipDto == null) {
            return R.failed("无权限");
        }
        //获取结束日期
        LocalDateTime endTime = vipDto.getEndTime();
        //计算还有几天截止
        // 计算差异
        long daysBetween = Duration.between(now, endTime).toDays();
        if(daysBetween<0L){
            return R.failed("您的权限已到期！");
        }
        return R.success(daysBetween);
    }

    @Override
    public R<List<InvitedUserDTO>> getUserIdByName(String realName) {
        //根据姓名查询用户id
        List<InvitedUserDTO> invitedUser = userMapper.getInvitedUser(realName);
        if (invitedUser.size()==0){
            return R.failed("没有查询到此用户！");
        }
        return R.success(invitedUser);

    }

    @Override
    public R<Map<String, Object>> getUserLoginLog(Integer pageNum, Integer pageSize) {
        // 创建分页对象
        Page<UserLoginLogDto> page = new Page<>(pageNum, pageSize, true);

        // 调用 Mapper 方法进行联查
        List<UserLoginLogDto> loginLogDtos = loginLogMapper.selectUserLoginLog(page).getRecords();


        // 获取总记录数
        long totalCount = page.getTotal();

        // 使用 Log 记录日志
        log.info("总共找到记录数：{}", totalCount);
        log.info("当前页码：{}", page.getCurrent());
        log.info("总页数：{}", page.getPages());

        // 构造返回结果
        Map<String, Object> result = new HashMap<>();
        result.put("userLoginLogs", loginLogDtos);
        result.put("totalCount", totalCount);

        return R.success(result);
    }

    @Override
    public R<Map<String, Object>> getAdminLoginLog(Integer pageNum, Integer pageSize) {
        Page<AdminLoginLog> page = new Page<>(pageNum, pageSize, true);
        QueryWrapper<AdminLoginLog> QueryWrapper = new QueryWrapper<>();
        IPage<AdminLoginLog> adminLoginLogIPage = adminLoginLogMapper.selectPage(page, QueryWrapper);
        List<AdminLoginLog> adminLoginLogs = adminLoginLogIPage.getRecords();
        long totalCount = adminLoginLogIPage.getTotal();
        // 使用 Log 记录日志
        log.info("总共找到记录数：{}", totalCount);
        log.info("当前页码：{}", page.getCurrent());
        log.info("总页数：{}", page.getPages());

        Map<String, Object> result = new HashMap<>();
        result.put("adminLoginLogs", adminLoginLogs);
        result.put("totalCount", totalCount);

        return R.success(result);
    }


}
